﻿using DeHeng_Mes.Constant;
using DeHeng_Mes.Dao.PlcDao;
using DeHeng_Mes.Model.PlcModel.door_01;
using S7.Net;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace DeHeng_Mes.Utils
{
    public static class PlcDataUtil
    {
        /// <summary>
        /// 解析Plc中String数据
        /// </summary>
        /// <param name="bytes"></param>
        /// <param name="startOff"></param>
        /// <param name="off"></param>
        /// <returns></returns>
        public static string ArrayToString(byte[] bytes, int startOff, int off)
        {
            // 将字节数组解析为字符串
            string stringData = Encoding.UTF8.GetString(bytes.Skip(startOff).Take(off).ToArray()).Replace("\0", "");

            return string.IsNullOrEmpty(stringData) ? "" : stringData;
        }

        /// <summary>
        /// 解析Plc中Byte数据
        /// </summary>
        /// <param name="bytes"></param>
        /// <param name="startOff"></param>
        /// <param name="off"></param>
        /// <returns></returns>
        public static byte ByteToString(byte[] bytes, int startOff, int off)
        {
            byte byteData = bytes[startOff];

            return byteData;
        }

        /// <summary>
        /// 解析Plc中Int数据
        /// </summary>
        /// <param name="bytes"></param>
        /// <param name="startOff"></param>
        /// <param name="off"></param>
        /// <returns></returns>
        public static short IntToShort(byte[] bytes, int startOff, int off)
        {
            // 将数组解析为Short
            short shortData = S7.Net.Types.Int.FromByteArray(bytes.Skip(startOff).Take(off).ToArray());

            return shortData;
        }

        /// <summary>
        /// 解析Plc中DateAndTime数据
        /// </summary>
        /// <param name="bytes"></param>
        /// <param name="startOff"></param>
        /// <param name="off"></param>
        /// <returns></returns>
        public static DateTime? DateAndTimeToDateTime(byte[] bytes, int startOff, int off)
        {
            // 将数组数据解析为DateTime
            DateTime dateTime = S7.Net.Types.DateTime.FromByteArray(bytes.Skip(startOff).Take(off).ToArray());

            return dateTime;
        }

        /// <summary>
        /// 解析Plc中DateTimeLong数据
        /// </summary>
        /// <param name="bytes"></param>
        /// <param name="startOff"></param>
        /// <param name="off"></param>
        /// <returns></returns>
        public static DateTime? DateTimeLongToDateTime(byte[] bytes, int startOff, int off)
        {
            // 将数组数据解析为DateTime
            DateTime dateTime = S7.Net.Types.DateTimeLong.FromByteArray(bytes.Skip(startOff).Take(off).ToArray());

            return dateTime;
        }

        /// <summary>
        /// 根据传入字段来分配数据调用方法
        /// </summary>
        /// <param name="fieldType"></param>
        /// <param name="bytes"></param>
        /// <param name="dicData"></param>
        /// <returns></returns>
        public static object GetData(Type fieldType, byte[] bytes, KeyValuePair<string, Tuple<int, int>> dicData)
        {
            // 获取具体偏移量值
            Tuple<int, int> offValue = dicData.Value;

            // 初始化返回值
            object data = null;

            // 根据字段类型判断所调用方法
            switch (fieldType.Name)
            {
                case "String":

                    if (dicData.Key.Contains("CODE"))
                        data = ArrayToString(bytes, offValue.Item1, offValue.Item2);
                    else
                        data = ArrayToString(bytes, offValue.Item1 + 2, offValue.Item2);

                    break;
                case "Nullable`1":

                        data = DateAndTimeToDateTime(bytes, offValue.Item1, offValue.Item2);

                    break;
                case "DateTime":

                        data = DateAndTimeToDateTime(bytes, offValue.Item1, offValue.Item2);

                    break;
                case "Int16":

                    data = IntToShort(bytes, offValue.Item1, offValue.Item2);

                    break;
                case "Byte":

                    data = ByteToString(bytes, offValue.Item1, offValue.Item2);

                    break;
            }

            return data;
        }

        /// <summary>
        /// 通用方法：通过反射机制设置字段值
        /// </summary>
        /// <param name="bytes"></param>
        /// <param name="dic"></param>
        /// <returns></returns>
        public static StationData FromBytesToClass(byte[] bytes, Dictionary<string, Tuple<int, int>> dic, string className)
        {
            StationData Class = new StationData();

            Type type = Class.GetType();

            FieldInfo[] fieldInfos = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

            foreach (FieldInfo fieldInfo in fieldInfos)
            {
                foreach (var dicData in dic)
                {
                    if (fieldInfo.Name.Contains(dicData.Key))
                    {

                        // 获取字段的数据类型
                        Type fieldType = fieldInfo.FieldType;

                        // 根据不同数据类型调用不同的方法来获取数据
                        object data = GetData(fieldType, bytes, dicData);

                        if (data != null)
                        {
                            Type targetType = Nullable.GetUnderlyingType(fieldInfo.FieldType) ?? fieldInfo.FieldType;

                            object safeValue = Convert.ChangeType(data, targetType);

                            fieldInfo.SetValue(Class, safeValue);
                        }
                    }
                }
            }

            return Class;
        }

        /// <summary>
        /// 根据索引查找Plc中的工单数据
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public static List<string> GetOrderData(string materialId, int index)
        {
            List<string> plcNames = new List<string>();

            foreach (var plcConfig in PlcConnectionManager.plcContainer)
            {
                byte[] materialCodeBytes = plcConfig.plc.ReadBytes(DataType.DataBlock, SystemConstant.ORDER_DB, index == 1 ? SystemConstant.ORDER_MATERIALCODE_OFF : SystemConstant.ORDER_MATERIALCODE_OFF + ((index - 1) * 516), 256);

                string materialCode = PlcDao.ParseS7String(materialCodeBytes);

                if (string.IsNullOrEmpty(materialId))
                {
                    continue;
                }
                else if (materialId.Equals(materialCode))
                {
                    plcNames.Add(plcConfig.name);
                }
            }

            return plcNames;
        }
    }
}
